﻿using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Caching;
using RefLib.Data;

namespace RefLib.Branding
{
    public class BrandingService : IBrandingService, IDisposable
    {
        public void Dispose()
        {
            if (_brandingContext != null)
            {
                _brandingContext.Dispose();
                _brandingContext = null;
            }
        }

        public BlobModel GetBlobByGuid(Guid blobGuid)
        {
            BrandedObjectDTO item = DB.GetBlobByGuid(blobGuid);
            return item.ToBlobModel();
        }

        public BlobModel GetBrandedBlob(string resourceId, string tag, string subTag, string targetBrand)
        {
            if (! HasCache)
            {
                BrandedObjectDTO item = DB.ResolveBlob(resourceId, tag, subTag, targetBrand);
                return (item != null) ? item.ToBlobModel() : null;
            }
            else
            {
                List<BrandedObjectDTO> filteredCollection = GetFromCache(BLOB_OBJECT_TYPE, resourceId, tag, subTag);
                BrandedObjectDTO item = GetBestMatch(filteredCollection, targetBrand);
                if (item == null)
                    return null;

                if ((item.Blob == null || item.Blob.Length == 0) && item.ContentLength > 0)
                {
                    item = DB.GetBlobById(item.ID);
                    SaveToCache(item);
                }

                return item.ToBlobModel();
            }
        }

        public string GetBrandedText(string resourceId, string tag, string subTag, string targetBrand, string defaultValue)
        {
            if (! HasCache)
            {
                BrandedObjectDTO item = DB.ResolveText(resourceId, tag, subTag, targetBrand);
                return (item != null) ? item.Text : defaultValue;
            }
            else
            {
                List<BrandedObjectDTO> filteredCollection = GetFromCache(TEXT_OBJECT_TYPE, resourceId, tag, subTag);
                BrandedObjectDTO item = GetBestMatch(filteredCollection, targetBrand);
                if (item == null)
                    return defaultValue;

                if (String.IsNullOrEmpty(item.Text) && item.ContentLength > 0)
                {
                    item = DB.GetTextById(item.ID);
                    SaveToCache(item);
                }

                return (item.Text != null) ? item.Text : defaultValue;
            }
        }

        public object GetBrandedValue(string resourceId, string tag, string subTag, string targetBrand, object defaultValue)
        {
            if (! HasCache)
            {
                BrandedObjectDTO item = DB.ResolveValue(resourceId, tag, subTag, targetBrand);
                return (item != null) ? ValueFactory.Build(item) : defaultValue;
            }
            else
            {
                List<BrandedObjectDTO> filteredCollection = GetFromCache(VALUE_OBJECT_TYPE, resourceId, tag, subTag);
                BrandedObjectDTO item = GetBestMatch(filteredCollection, targetBrand);
                if (item == null)
                    return defaultValue;

                if (String.IsNullOrEmpty(item.SerializedValue) && item.ContentLength > 0)
                {
                    item = DB.GetValueById(item.ID);
                    SaveToCache(item);
                }

                return ValueFactory.Build(item);
            }
        }

        internal BrandingContext DB
        {
            get
            {
                if (_brandingContext == null)
                    _brandingContext = ContextBuilder.Build<BrandingContext>(BrandingContext.BrandingConnection);
                return _brandingContext;
            }
        }

        protected bool HasCache
        {
            get
            {
                if (!_hasCache.HasValue)
                    _hasCache = ! (HttpContext.Current == null || HttpContext.Current.Cache == null);
                return _hasCache.Value;
            }
        }

        private string BuildKey(string tag, string subTag)
        {
            return String.Format("|{0}:{1}|", tag, subTag);
        }

        private BrandedObjectDTO GetBestMatch(List<BrandedObjectDTO> collection, string targetBrand)
        {
            if (collection == null || collection.Count == 0)
                return null;

            BrandedObjectDTO bestMatch = null;
            foreach (BrandedObjectDTO item in collection)
            {
                if (targetBrand.IndexOf(item.Brand) != -1 &&
                    (bestMatch == null || item.Brand.Length > bestMatch.Brand.Length))
                {
                    bestMatch = item;
                }
            }
            
            return bestMatch;
        }

        private List<BrandedObjectDTO> GetFromCache(string objectType, string resourceId, string tag, string subTag)
        {
            List<BrandedObjectDTO> unfilteredCollection = GetFromCache(tag, subTag);
            var filteredCollection = new List<BrandedObjectDTO>();
            foreach (BrandedObjectDTO item in unfilteredCollection)
            {
                if (item.ObjectType.IndexOf(objectType) == 0 && String.Compare(item.ResourceId, resourceId, true) == 0)
                    filteredCollection.Add(item);
            }
            return filteredCollection;
        }

        private List<BrandedObjectDTO> GetFromCache(string tag, string subTag)
        {
            Cache cache = HttpContext.Current.Cache;
            string key = BuildKey(tag, subTag);

            var unfilteredCollection = (List<BrandedObjectDTO>) cache[key];
            if (unfilteredCollection == null)
            {
                unfilteredCollection = DB.GetAllObjects(tag, subTag, DEFERRED_LOADING_LENGTH);
                DateTime expiration = DateTime.Today + new TimeSpan(24, 0, 0);
                cache.Add(key, unfilteredCollection, null, expiration, Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
            }
            return unfilteredCollection;
        }

        private void SaveToCache(BrandedObjectDTO item)
        {
            List<BrandedObjectDTO> unfilteredCollection = GetFromCache(item.Tag, item.SubTag);
            
            Nullable<int> foundIndex = null;;
            for (int i = 0; i < unfilteredCollection.Count; ++i)
            {
                if (unfilteredCollection[i].ObjectType.IndexOf(item.ObjectType) == 0 && unfilteredCollection[i].ID == item.ID)
                {
                    foundIndex = i;
                    break;
                }
            }
            
            if (foundIndex.HasValue)
                unfilteredCollection.RemoveAt(foundIndex.Value);
                
            unfilteredCollection.Add(item);
            string key = BuildKey(item.Tag, item.SubTag);
            HttpContext.Current.Cache[key] = unfilteredCollection;
        }

        private static readonly string BLOB_OBJECT_TYPE = "blob";
        private static readonly int DEFERRED_LOADING_LENGTH = 1000;
        private static readonly string TEXT_OBJECT_TYPE = "text";
        private static readonly string VALUE_OBJECT_TYPE = "value";

        private BrandingContext _brandingContext;
        private Nullable<bool> _hasCache;
    }
}
